#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <semaphore.h>
#include <pthread.h>

int phil_count = 5, max_table = 4, curr_table = 0;
pthread_mutex_t table_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t table_occupied = PTHREAD_COND_INITIALIZER;
pthread_mutex_t forks[5];

void pick_forks(int id) {
	if (pthread_mutex_lock(&table_mutex) != 0) {
		perror("pthread_mutex_lock");
		exit(1);
	}
	while (curr_table == max_table) {
		if (pthread_cond_wait(&table_occupied, &table_mutex) != 0) {
			perror("pthread_cond_wait");
			exit(2);
		}
		sleep(1);
	}
	curr_table++;
	if (pthread_mutex_unlock(&table_mutex) != 0) {
		perror("pthread_mutex_unlock");
		exit(3);
	}
	
	if (pthread_mutex_lock(&forks[id]) != 0) {
		perror("pthread_mutex_lock");
		exit(4);
	}
	printf("Filozof nr %d podniosl widelec nr %d\n", id, id);
	int tmpid = (id + 1)%phil_count;
	if (pthread_mutex_lock(&forks[tmpid]) != 0) {
		perror("pthread_mutex_lock");
		exit(5);
	}
	printf("Filozof nr %d podniosl widelec nr %d\n", id, tmpid);
}

void put_forks(int id) {
	if (pthread_mutex_lock(&table_mutex) != 0) {
		perror("pthread_mutex_lock");
		exit(-1);
	}
	int tmpid = (id + 1)%phil_count;
	printf("Filozof %d odklada widelce %d, %d\n", id, id, tmpid);
	if (pthread_mutex_unlock(&forks[tmpid]) != 0) {
		perror("pthread_mutex_unlock");
		exit(-2);
	}
	if (pthread_mutex_unlock(&forks[id]) != 0) {
		perror("pthread_mutex_unlock");
		exit(-3);
	}
	curr_table--;
	if (pthread_cond_broadcast(&table_occupied) != 0) {
		perror("pthread_cond_broadcast");
		exit(-4);
	}
	if (pthread_mutex_unlock(&table_mutex) != 0) {
		perror("pthread_mutex_unlock");
		exit(-5);
	}
}

void * philosopher(void * id_ptr) {
	int id = *(int *)id_ptr;
	while (1) {
		printf("Filozof nr %d: 'Jestem głodny'\n", id);
		pick_forks(id);
		printf("Filozof nr %d: 'Jem'\n", id);
		sleep(2);
		put_forks(id);
		printf("Filozof nr %d: 'Zjadlem'\n", id);
		sleep(2);
	}
}

int main(int argc, char ** argv) {
	int i;
	for (i = 0; i < phil_count; i++) {
		if (pthread_mutex_init(&forks[i], NULL) == -1) {
			perror("pthread_mutex_init");
			return -1;
		}
	}
	pthread_t threads[phil_count];
	for (i = 0; i < phil_count; i++) {
		if (pthread_create(&threads[i], NULL, philosopher, &i) == -1) {
			perror("pthread_create");
			return -2;
		}
	}
	for (i = 0; i < phil_count; i++) {
		if (pthread_join(threads[i], NULL) == -1) {
			perror("pthread_join");
			return -3;
		}
	}
	return 0;
}
